/*
 * irqmacro.h
 *
 *  Created on: 31.05.2010
 *      Author: Phil
 */

#ifndef IRQMACRO_H_
#define IRQMACRO_H_

#define ARM_MODE_ABT     0x17
#define ARM_MODE_FIQ     0x11
#define ARM_MODE_IRQ     0x12
#define ARM_MODE_SVC     0x13

#define I_BIT            0x80
#define F_BIT            0x40

#define AT91C_BASE_AIC_ASM   0xFFFFF000 // (AIC) Base Address
#define AIC_IVR_ASM         0x00000100 // (AIC_IVR) IRQ Vector Register
#define AIC_EOICR_ASM      	0x00000130 // (AIC_EOICR) End of Interrupt Command Register

#define __STR_EXPAND(tok) #tok
#define __STR(tok) __STR_EXPAND(tok)

#define IRQ_WRAPPER(wrapper,handler)									\
	void wrapper( void ) __attribute__((naked));						\
	void wrapper( void )												\
	{																	\
	asm volatile (														\
    /* Save interrupt context on the stack to allow nesting */			\
        "sub     lr, lr, #4										\n\t"	\
        "stmfd   sp!, {lr}										\n\t"	\
        "mrs     lr, SPSR										\n\t"	\
        "stmfd   sp!, {r0, lr}									\n\t"	\
    /* Write in the IVR to support Protect Mode */						\
        "ldr     lr, ="__STR(AT91C_BASE_AIC_ASM)"   			\n\t"	\
        "ldr     r0, ="__STR(handler) "							\n\t"	\
        "str     lr, [r14, #"__STR(AIC_IVR_ASM) "]				\n\t"	\
    /* Branch to interrupt handler in Supervisor mode */				\
        "msr     CPSR_c, #"__STR(ARM_MODE_SVC) "				\n\t"	\
        "stmfd   sp!, {r1-r3, r4, r12, lr}						\n\t"	\
        "mov     lr, pc											\n\t"	\
        "bx      r0												\n\t"	\
        "ldmia   sp!, {r1-r3, r4, r12, lr}						\n\t"	\
        "msr     CPSR_c, #"__STR(ARM_MODE_IRQ)" | "__STR(I_BIT)"\n\t"	\
    /* Acknowledge interrupt */											\
        "ldr     lr, ="__STR(AT91C_BASE_AIC_ASM)"				\n\t"	\
        "str     lr, [r14, #"__STR(AIC_EOICR_ASM)"]				\n\t"	\
    /* Restore interrupt context and branch back to calling code */		\
        "ldmia   sp!, {r0, lr}									\n\t"	\
        "msr     SPSR_cxsf, lr									\n\t"	\
        "ldmia   sp!, {pc}^										\n\t"	\
        );																\
    }

#endif /* IRQMACRO_H_ */
